
import java.awt.*;
import javax.swing.*;

// a class representing an AVL array paired with a GUI representation
public class AVLArray extends JPanel {
	String identifier;
	AVLBasicType[] contents;
	Color background;
	boolean gradient = false;
	final Color BACKGROUND = Color.WHITE;
	final Color FOREGROUND = Color.BLACK;
	final int XBUFFER = 5;
	final int YBUFFER = 5;
	final int TEXTHEIGHT = 15;
	private static final long serialVersionUID = 1L;

	// constructs an AVLArray
	public AVLArray(String identifier, AVLBasicType[] contents) {
		super();
		this.identifier = identifier;
		this.setPreferredSize(new Dimension(contents.length*(contents[0].getWidth() + this.XBUFFER) + this.XBUFFER, contents[0].getHeight()+2*this.YBUFFER+this.TEXTHEIGHT));
		this.setMinimumSize(new Dimension(contents.length*(contents[0].getWidth() + this.XBUFFER) + this.XBUFFER, contents[0].getHeight()+2*this.YBUFFER+this.TEXTHEIGHT));
		this.setSize(new Dimension(contents.length*(contents[0].getWidth() + this.XBUFFER) + this.XBUFFER, contents[0].getHeight()+2*this.YBUFFER+this.TEXTHEIGHT));
		this.contents = contents;
		this.background = this.BACKGROUND;
	}

	// enable or disable gradient shading
	public void enableGradient(boolean gradient) {
		this.gradient = gradient;
		if(gradient)
			this.normalize();
		else
			for(int i = 0; i < contents.length; i++){
				contents[i].setAVLBackground(new Color(255, 255, 255));
			}
	}
	
	// returns an AVLBasicType in the AVLArray and highlights its GUI representation, if any
	public Object get(int i) {
		Object value = contents[i].get();
		this.repaint();
		return value;
	}

	// sets an AVLBasicType in the AVLArray and highlights its GUI representation, if any
	public void set(int i, Object value) {
		contents[i].set(value);
		if(gradient)
			this.normalize();
		this.repaint();
	}
	
	// swaps two values in the AVLArray and highlights their GUI representations, if any
	public void swap(int i1, int i2) {
		Object temp1 = contents[i1].get();
		Object temp2 = contents[i2].get(); 
		contents[i1].set(temp2);
		contents[i2].set(temp1);
		if(gradient)
			this.normalize();
		this.repaint();
	}

	// resets the GUI representation of this AVLArray, if any
	public void reset() {
		for(int i = 0; i < contents.length; i++){
			contents[i].reset();
		}
		this.repaint();
	}

	// sets up gradient shading based on current element values
	public void normalize() {

		// check if this is an array of integers
		if(contents[0].getSilently() instanceof Integer) {

			// determine the minimum and maximum values
			int min = Integer.MAX_VALUE;; 
			int max = Integer.MIN_VALUE;
			for(int i = 0; i < contents.length; i++) {
				if(min > (Integer)contents[i].getSilently())
					min = (Integer)contents[i].getSilently();
				if(max < (Integer)contents[i].getSilently())
					max = (Integer)contents[i].getSilently();
			}

			// create yellow gradient
			for(int i = 0; i < contents.length; i++) {
				contents[i].setAVLBackground(new Color(255, 255, 255-(int)Math.floor(((((Integer)contents[i].getSilently() - min) / (double)(max - min)) * 255))));
			}
		}

		// check if this is an array of doubles
		else if(contents[0].getSilently() instanceof Double) {

			// determine the minimum and maximum values
			double min = Double.MAX_VALUE; 
			double max = Double.MIN_VALUE;
			for(int i = 0; i < contents.length; i++) {
				if(min > (Double)contents[i].getSilently())
					min = (Double)contents[i].getSilently();
				if(max < (Double)contents[i].getSilently())
					max = (Double)contents[i].getSilently();
			}

			// create yellow gradient
			for(int i = 0; i < contents.length; i++) {
				contents[i].setAVLBackground(new Color(255, 255, 255-(int)Math.floor(((((Integer)contents[i].getSilently() - min) / (double)(max - min)) * 255))));
			}
		}

		// do nothing if array does not contain numbers
		else return;
	}

	// draws an AVLArray GUI representation
	public void paint(Graphics g) {

		// draw background
		Rectangle r = new Rectangle(0, 0, this.getPreferredSize().width, this.getPreferredSize().height);
		g.setColor(background);
		g.fillRect(r.x, r.y, r.width-1, r.height-1);

		// draw border
		g.setColor(this.FOREGROUND);
		g.drawRect(r.x, r.y, r.width-1, r.height-1);

		// draw identifier
		FontMetrics fm = g.getFontMetrics();
		g.drawString(identifier, r.x+r.width/2-fm.stringWidth(identifier)/2, r.y+15);

		// draw contents
		for(int i = 0; i < contents.length; i++) {
			contents[i].paint(g.create(this.XBUFFER + i*(contents[i].getWidth()+this.XBUFFER), 20, contents[i].getWidth(), contents[i].getHeight()));
		}
	}
}
